#!/usr/bin/env bash

# netflix pro seu terminal... ou quase isso
#
# Para detalhes sobre a licença veja o arquivo COPYING

# Adiciona caracteres em um linha
# Argumentos:
#   max
#   start
#   char
# Retorno:
#   Nenhum
fill_line() {
  declare -i start max
  local char

  max=${1}
  start=${2:-0}
  char="${3:- }"

  printf "%.s${char}" $(seq ${start} ${max})
}

# Desenha corpo (background + borda) do layout de acordo com o número de linhas
# Globais:
#   GEO[HD_W]
#   GEO[HD_P]
#   C[BORDER]
# Argumentos:
#   start
#   lines
#   color
# Retorno:
#   Nenhum
draw() {
  local spc bord bdy border hdrw
  declare -i start lines color

  start=${1}
  lines=${2}
  color=${3}

  (( lines+=start ))
  hdrw=$(( GEO[HD_W]-GEO[HD_P] ))
  spc="$(fill_line ${hdrw} 1)"
  bord="$(fill_line ${hdrw} 1 ▀)"

  bdy="${C[BORDER]} \e[48;5;${color}m${spc}${C[BORDER]} \n"
  border="${C[BORDER]} \e[38;5;${color}m${bord}${C[BORDER]} \e[48;5;${color}m"

  cup 0 ${start}
  tput ed

  for (( i=start; i <lines; i++ )); do
      printf "${bdy}"
  done

  printf "${border}"
}

# Posiciona cursor na tela. Wrapper para 'tput cup' para passar X e Y com offset
# Argumentos:
#   x
#   y
# Returno:
#   Nenhum
cup() {
  local x y
  x="${1}"
  y="${2}"

  tput cup ${y} ${x}
}

# Retorna o valor necessário para alinhar um elemento horizontalmente no centro
# de 80 colunas.
# Globais:
#   GEO[BD_W]
# Argumentos:
#   width
# Retorno:
#   valor para o alinhamento lateral
center() {
  declare -i width
  width="${1}"

  printf "%d" $(((GEO[BD_W]/2) - (width/2)))
}

# Imprime o header
# Argumentos:
#   Nenhum
# Retorno:
#   Nenhum
header() {
  declare -a logo
  declare -i x y

  tput civis

  logo[0]="${C[BORDER]} ${C[RED]}▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄${C[BORDER]} ${E[RST]}"
  logo[1]="${C[BORDER]} ${C[BG_RED]}${C[WHT]}                                                                              ${C[BORDER]} ${E[RST]}"
  logo[2]="${C[BORDER]} ${C[BG_RED]}${C[WHT]}   ▄    ▄            ▄     ▄▄▄▄▄▄           ▄                    ▄            ${C[BORDER]} ${E[RST]}"
  logo[3]="${C[BORDER]} ${C[BG_RED]}${C[WHT]}   █${C[BLK]}█${C[WHT]}   █${C[BLK]}█${C[WHT]}           █${C[BLK]}█${C[WHT]}    █${C[BLK]}█${C[WHT]}${C[BLK]}▀${C[WHT]}${C[BLK]}▀${C[WHT]}${C[BLK]}▀${C[WHT]}${C[BLK]}▀${C[WHT]}${C[BLK]}▀${C[WHT]}          ▀${C[BLK]}█${C[WHT]}                   ▀${C[BLK]}█${C[WHT]}           ${C[BORDER]} ${E[RST]}"
  logo[4]="${C[BORDER]} ${C[BG_RED]}${C[WHT]}   █${C[BG_BLK]}▀${C[BG_RED]}▄  █${C[BLK]}█${C[WHT]} ▄▀${C[BG_BLK]}▀${C[BG_RED]}${C[BG_BLK]}▀${C[BG_RED]}${C[BG_BLK]}▀${C[BG_RED]}▄  ▀${C[BG_BLK]}▀${C[BG_RED]}█${C[BG_BLK]}▀${C[BG_RED]}${C[BG_BLK]}▀${C[BG_RED]}${C[BLK]}▄${C[WHT]}  █${C[BG_BLK]}▄${C[BG_RED]}▄▄    █▄▀${C[BG_BLK]}▀${C[BG_RED]}${C[BG_BLK]}▀${C[BG_RED]}${C[BG_BLK]}▀${C[BG_RED]}${C[BLK]}▄${C[WHT]} ▀█${C[BLK]}▄${C[WHT]}  ▄▀${C[BG_BLK]}▀${C[BG_RED]}${C[BG_BLK]}▀${C[BG_RED]}${C[BG_BLK]}▀${C[BG_RED]}█${C[BLK]}▄${C[WHT]} █${C[BLK]}▄${C[WHT]}   █${C[BLK]}▄${C[WHT]} ▀█${C[BLK]}▄${C[WHT]}  ▄▀${C[BG_BLK]}▀${C[BG_RED]}${C[BG_BLK]}▀${C[BG_RED]}${C[BG_BLK]}▀${C[BG_RED]}${C[BG_BLK]}▀${C[BG_RED]}${C[BLK]}▄${C[WHT]}  ${C[BORDER]} ${E[RST]}"
  logo[5]="${C[BORDER]} ${C[BG_RED]}${C[WHT]}   █${C[BLK]}█${C[WHT]}${C[BLK]}▀${C[WHT]}${C[BG_BLK]}▀${C[BG_RED]}▄█${C[BLK]}█${C[WHT]} █${C[BG_BLK]}▄${C[BG_RED]}▄▄▄█${C[BLK]}█${C[WHT]}   █${C[BLK]}█${C[WHT]}    █${C[BLK]}█${C[WHT]}${C[BLK]}▀${C[WHT]}${C[BLK]}▀${C[WHT]}${C[BLK]}▀${C[WHT]}   █${C[BLK]}█${C[WHT]}${C[BLK]}▀${C[WHT]}      █${C[BLK]}█${C[WHT]}  █${C[BLK]}█${C[WHT]}   █${C[BLK]}█${C[WHT]} █${C[BLK]}█${C[WHT]}   █${C[BLK]}█${C[WHT]}  █${C[BLK]}█${C[WHT]}  ▀${C[BG_BLK]}▄${C[BG_RED]}▄▄▄    ${C[BORDER]} ${E[RST]}"
  logo[6]="${C[BORDER]} ${C[BG_RED]}${C[WHT]}   █${C[BLK]}█${C[WHT]}  ${C[BLK]}▀${C[WHT]}█${C[BLK]}█${C[WHT]} █${C[BLK]}█${C[WHT]}${C[BLK]}▀${C[WHT]}${C[BLK]}▀${C[WHT]}${C[BLK]}▀${C[WHT]}${C[BLK]}▀${C[WHT]}${C[BLK]}▀${C[WHT]}   █${C[BLK]}█${C[WHT]}    █${C[BLK]}█${C[WHT]}      █${C[BLK]}█${C[WHT]}       █${C[BLK]}█${C[WHT]}  █${C[BLK]}█${C[WHT]}   █${C[BLK]}█${C[WHT]} █${C[BLK]}█${C[WHT]}   █${C[BLK]}█${C[WHT]}  █${C[BLK]}█${C[WHT]}    ${C[BLK]}▀${C[WHT]}${C[BLK]}▀${C[WHT]}${C[BLK]}▀${C[WHT]}█${C[BLK]}▄${C[WHT]}  ${C[BORDER]} ${E[RST]}"
  logo[7]="${C[BORDER]} ${C[BG_RED]}${C[WHT]}   ▀${C[BLK]}█${C[WHT]}   ▀${C[BLK]}█${C[WHT]}  ▀${C[BG_BLK]}▀${C[BG_RED]}${C[BG_BLK]}▀${C[BG_RED]}${C[BG_BLK]}▀${C[BG_RED]}${C[BLK]}▄${C[WHT]}     ▀${C[BG_BLK]}▀${C[BG_RED]}${C[BG_BLK]}▀${C[BG_RED]}${C[BLK]}▄${C[WHT]} ▀${C[BLK]}█${C[WHT]}      ▀${C[BLK]}█${C[WHT]}      ▀${C[BG_BLK]}▀${C[BG_RED]}${C[BG_BLK]}▀${C[BG_RED]}${C[BLK]}▄${C[WHT]}  ▀${C[BG_BLK]}▀${C[BG_RED]}${C[BG_BLK]}▀${C[BG_RED]}${C[BG_BLK]}▀${C[BG_RED]}█${C[BLK]}█${C[WHT]}  ▀${C[BG_BLK]}▀${C[BG_RED]}${C[BG_BLK]}▀${C[BG_RED]}${C[BG_BLK]}▀${C[BG_RED]}${C[BG_BLK]}▀${C[BG_RED]}${C[BLK]}█${C[WHT]} ▀${C[BG_BLK]}▀${C[BG_RED]}${C[BG_BLK]}▀${C[BG_RED]}${C[BLK]}▄${C[WHT]} ▀${C[BG_BLK]}▀${C[BG_RED]}${C[BG_BLK]}▀${C[BG_RED]}${C[BG_BLK]}▀${C[BG_RED]}${C[BG_BLK]}▀${C[BG_RED]}${C[BLK]}▄${C[WHT]}${C[BLK]}▀${C[WHT]}  ${C[BORDER]} ${E[RST]}"
  logo[8]="${C[BORDER]} ${C[BG_RED]}${C[WHT]}                                                     █${C[BLK]}█${C[WHT]}             v${VERSION}    ${C[BORDER]} ${E[RST]}"
  logo[9]="${C[BORDER]} ${C[BG_RED]}${C[WHT]}                                                      ${C[BLK]}▀${C[WHT]}                       ${C[BORDER]} ${E[RST]}"

  x=0

  for (( y=0; y <=9; y++ )); do
    cup ${x} ${y}
    printf "%s" "${logo[y]}";
  done

  tput cnorm
}

# Imprime erro e saí
# Argumentos:
#   err_ext
#   err_msg
#   err_cmd
# Retorno:
#   Nenhum
error() {
  local err_ext err_msg err_cmd

  err_ext="${1}"
  err_msg="${2}"
  err_cmd="${3}"

  printf "\n%s\n\n" "${err_msg}" >&2

  [[ "${err_cmd}" == usage ]] && usage >&2

  exit "${err_ext}"
}

# Retorna valor para posicionar o prompt verticalmente
# Globais:
#   GEO[HD_S]
# Argumentos:
#   length
# Retorno:
#   valor para posicionar o prompt
prompt_vertical_pos() {
  local -i length

  length=${1}
  printf "%d" $(( GEO[HD_S]+length+2 ))
}

# Toca o stream
# Globais:
#   GEO[HD_S]
# Argumentos:
#   stream_name
#   stream_url
# Retorno:
#   Nenhum
play() {
  local stream_name stream_url
  declare -i x y

  stream_name="${1}"
  stream_url="${2}"

  GEO[MG_T]=$(prompt_vertical_pos ${#list_name[@]})
  cup ${GEO[MG_L]} ${GEO[MG_T]}

  printf "Reproduzindo: %s" "${stream_name}"

  if [[ "$(tty)" =~ "tty" ]]; then
    tput civis
    mplayer -fs -vo fbdev2 -vf scale -zoom "${stream_url}" &> /dev/null
    tput cnorm
  else
    mplayer -slave -input file=/tmp/mplayer "${stream_url}" &> /dev/null
  fi
}

# Imprime breadcrumb
# Globais:
#   BREADCRUMB
#   GEO[HD_W]
#   GEO[HD_P]
#   C[BORDER]
#   C[BG_WHT]
#   BREADCRUMB
# Argumentos:
#   Nenhum
# Retorno:
#   Nenhum
breadcrumb() {
  declare -i i fill breadcrumb_padding
  local spc

  tput civis
  cup 1 ${GEO[HD_H]}

  if [[ -z ${BREADCRUMB[1]} ]]; then

    breadcrumb_padding=2
    fill=$(( GEO[HD_W]-GEO[HD_P]-${#BREADCRUMB[0]}-breadcrumb_padding ))
    spc="$(fill_line ${fill})"

    printf "${C[BG_WHT]}${C[RED]} %s%s${C[BORDER]}" "${BREADCRUMB[0]}" "${spc}"

  elif [[ -n ${BREADCRUMB[1]} ]]; then

    breadcrumb_padding=5
    fill=$(( GEO[HD_W]-GEO[HD_P]-${#BREADCRUMB[0]}-${#BREADCRUMB[1]}-breadcrumb_padding ))
    spc="$(fill_line ${fill})"

    printf "\e[K${C[BG_WHT]}${C[RED]} %s > %s%s${C[BORDER]} " "${BREADCRUMB[@]}" "${spc}"

  fi

  tput cnorm
}

# Gera menu
# Globais:
#   GEO[MG_L]
#   GEO[MG_T]
#   GEO[HD_H]
#   GEO[BD_H]
#   C[NRM]
#   C[SLT]
#   C[NRM]
# Argumentos:
#   options
#   color
# Retorno:
#   Nenhum
menu() {
  declare -n options
  declare -i color start max x y lines
  local spc

  options="${1}"
  color="${2}"

  tput civis

  ((array_length=${#options[@]}))
  ((max=${#array_length}))

  tput setab ${color}

  GEO[MG_T]=$(prompt_vertical_pos ${#options})
  cup ${GEO[MG_L]} ${GEO[MG_T]}

  tput ed
  lines=1
  start=$(( GEO[HD_H]+lines ))
  draw ${start} ${GEO[BD_H]} "${color}"

  lines=2
  GEO[MG_T]=$(( GEO[HD_H]+lines ))
  cup ${GEO[MG_L]} ${GEO[MG_T]}

  if (( array_length < ${RESULT_LIMIT} )); then
    for (( i=1; i < array_length; i++ )); do
      spc="$(fill_line $(( max-${#i} )))"
      printf "${C[NRM]}[${C[SLT]}%s${C[NRM]}]%s%s" ${i} "${spc}" "${options[i]}"
      tput cud1
      tput cuf 2
    done
  else
    for (( i=1; i < ${RESULT_LIMIT}; i++ )); do
      spc="$(fill_line $(( max-${#i} )))"
      printf "${C[NRM]}[${C[SLT]}%s${C[NRM]}]%s%s" ${i} "${spc}" "${options[i]}"
      tput cud1
      tput cuf 2
    done
  fi

  tput cud 1
  spc="$(fill_line $(( --max )))"
  printf "${C[NRM]}[${C[SLT]}0${C[NRM]}]%s%s" "${spc}" "${options[0]}"

  tput cnorm
}

# Retorna link de um vídeo
# Argumentos:
#   id
# Retorno:
#   url do vídeo
get_link() {
  local id url

  id="${1}"
  if [[ "http" =~ "${id}" ]]; then
    url="${id}"
  else
    url="https://www.blogger.com/video-play.mp4?contentId=${id}"
  fi

  printf "%s" "${url}"
}

# Busca videos no banco de dados
# Globais:
#   GEO[MG_L]
#   GEO[MG_T]
#   BREADCRUMB
#   C[MN_SRCH]
#   GEO[HD_S]
#   EXIT[OK]
# Argumentos:
#   search_video
# Retorno:
#   Nenhum
search() {
  local search_video search_result choice url
  declare -a list_name url
  declare -i array_length x y start max

  search_video="${1}"
  list_name[0]="Retornar"
  search_result=$(grep -i "${search_video}" "${DB}")

  if [[ -z ${search_result:+x} ]]; then

    GEO[MG_T]=$(prompt_vertical_pos ${#opts})
    cup ${GEO[MG_L]} ${GEO[MG_T]}

    printf "Desculpe, nenhum resultado encontrado."
    read

    return "${EXIT[ERR]}"
  fi

  mapfile -O 1 -t list_name <<<"$(cut -d\^ -f2-3 <<<"${search_result}")"
  mapfile -O 1 -t url <<<"$(cut -d\^ -f4 <<<"${search_result}")"

  BREADCRUMB[1]="Busca: ${search_video}"

  breadcrumb

  tput setab ${C[MN_SRCH]}
  tput ed

  cup ${GEO[MG_L]} ${GEO[HD_H]}

  menu list_name ${C[MN_SRCH]}

  GEO[MG_T]=$(prompt_vertical_pos ${#list_name[@]})
  while :; do
    cup ${GEO[MG_L]} ${GEO[MG_T]}
    tput ech 77

    printf "Escolha uma opção: " choice
    read choice

    [[ " ${!list_name[@]} " =~ " ${choice} " ]] || continue

    [[ ${choice} == "0" ]] && return ${EXIT[OK]}

    url=$(get_link "${url[choice]}")
    play "${list_name[choice]}" "${url}"
  done
}

# Cria menu de episódios de desenhos ou séries
# Globais:
#   BREADCRUMB
#   C[MN_SRCH]
#   GEO[MG_L]
#   GEO[MG_T]
#   EXIT[OK]
# Argumentos:
#   title
#   color
# Retorno:
#   Nenhum
get_episodes() {
  local title color search_result
  local -a list_name url

  title="${1}"
  color="${2}"

  search_result=$(grep -i "${title}" "${DB}")

  list_name[0]="Retornar"
  mapfile -O 1 -t list_name <<<"$(cut -d\^ -f3 <<<"${search_result}")"
  mapfile -O 1 -t url <<<"$(cut -d\^ -f4 <<<"${search_result}")"

  BREADCRUMB[1]="${title}"

  breadcrumb

  tput setab ${color}
  tput ed

  cup ${GEO[MG_L]} ${GEO[HD_H]}

  menu list_name ${color}

  GEO[MG_T]=$(prompt_vertical_pos ${#list_name[@]})
  while :; do
    cup ${GEO[MG_L]} 34
    tput ech 77

    printf "Escolha uma opção: " choice
    read choice

    [[ " ${!list_name[@]} " =~ " ${choice} " ]] || continue

    [[ ${choice} == "0" ]] && return ${EXIT[OK]}

    url=$(get_link ${url[choice]})
    play "${list_name[choice]}" "${url}"
  done
}

# Gera menus para categorias
# Globais:
#   BREADCRUMB
#   C[MN_TOON]
#   GEO[MG_L]
#   GEO[MG_T]
#   EXIT[OK]
# Argumentos:
#   category
#   abbr
#   color
# Retorno:
#   Nenhum
generate_menu() {
  local choice category abbr color
  local -a opts

  category="${1}"
  abbr="${2}"
  color="${3}"

  BREADCRUMB[1]="${category}"
  breadcrumb

  opts[0]="Retornar"
  mapfile -O 1 -t opts <<<"$(grep "${abbr}^" database | cut -d\^ -f2 \
    | sort -V | uniq | less)"

  while :; do
    menu opts ${color}

    if (( ${#opts[@]} < ${RESULT_LIMIT} )); then
      GEO[MG_T]=$(prompt_vertical_pos ${#opts[@]})
      cup ${GEO[MG_L]} ${GEO[MG_T]}
      tput ech 77
    else
      GEO[MG_T]=$(prompt_vertical_pos 20)
      cup ${GEO[MG_L]} ${GEO[MG_T]}
      tput ech 77
    fi

    printf "Escolha uma opção: "
    read choice

    [[ " ${!opts[@]} " =~ " ${choice} " ]] || continue

    [[ ${choice} == "0" ]] && return ${EXIT[OK]}

    get_episodes "${opts[choice]}" ${color}
  done
}

# Imprime o número de linhas e colunas do terminal
# Globais:
#   GEO[WD_W]
#   GEO[WD_H]
# Argumentos:
#   Nenhum
# Retorno:
#   Nenhum
get_window_size() {
  GEO[WD_W]=$(tput cols)
  GEO[WD_H]=$(tput lines)
}

main() {
  readonly VERSION="0.1.0" DB="database"
  readonly -A EXIT=( [OK]=0 [ERR]=1 )

  local dependencies

  dependencies=("mplayer")

  [[ -f "${DB}" ]] ||
      error ${EXIT_[ERR]} "Banco de dados <${DB}> não foi encontrado"

  for dependency in "${dependencies[@]}"; do
    type "${dependency}" &>/dev/null ||
        error ${EXIT_[ERR]} "${dependency} não foi encontrado."
  done

  declare -rA C=(
    [RED]=$'\e[38;5;160m' [BORDER]=$'\e[48;5;232m'
    [WHT]=$'\e[38;5;231m' [BG_RED]=$'\e[48;5;160m'
    [BLK]=$'\e[38;5;16m'  [BG_WHT]=$'\e[48;5;231m'
    [NRM]=$'\e[38;5;177m' [BG_BLK]=$'\e[48;5;16m'
    [SLT]=$'\e[38;5;50m'  [BG_GRY]=$'\e[48;5;240m'
    [MN_MAIN]=240 [MN_SRCH]=238 [MN_TOON]=242
    [MN_SERS]=235
  )

  declare -rA E=(
    [RST]=$'\e[m' [CPL]=$'\e[F'
    [CNL]=$'\e[E' [CHA]=$'\e[G'
  )

  local opts choice search_video
  declare -a BREADCRUMB
  declare -i i x y line_space
  declare -r RESULT_LIMIT=20

  declare -A GEO=(
    [WN_W]=0  [WN_H]=0
    [HD_W]=80 [HD_H]=10 [HD_P]=2 [HD_S]=0
    [BD_W]=80 [BD_H]=0
    [MG_T]=0  [MG_B]=2  [MG_L]=2
  )

  trap 'get_window_size' WINCH

  get_window_size

  opts=("Sair" "Ajuda" "Busca" "Desenhos" "Series")

  tput smcup
  clear

  (( GEO[HD_S]=GEO[HD_H]+GEO[MG_B] ))

  header

  (( GEO[BD_H]=GEO[WD_H]-GEO[HD_S] ))

  while :; do

    if [[ -z ${BREADCRUMB[0]} || -n ${BREADCRUMB[1]} ]]; then

      BREADCRUMB[0]="Principal"
      breadcrumb
      menu opts ${C[MN_MAIN]}

      GEO[MG_T]=$(prompt_vertical_pos ${#opts[@]})
      cup ${GEO[MG_L]} ${GEO[MG_T]}

    else

      GEO[MG_T]=$(prompt_vertical_pos ${#opts})
      cup ${GEO[MG_L]} ${GEO[MG_T]}

    fi

    tput sc
    printf "Escolha uma opção: "
    line_space=$(( GEO[BD_W]-24 ))
    tput ech ${line_space}

    read -n1 choice

    [[ " ${!opts[@]} " =~ " ${choice} " ]] || continue

    case "${choice}" in
      0) clear; tput rmcup; tput cnorm; exit "${EXIT[OK]}" ;;
      1) ;;
      2)
        tput rc
        printf "Digite o nome do vídeo: "
        line_space=$(( GEO[BD_W]-30 ))
        tput ech ${line_space}

        read search_video

        [[ -n "${search_video}" ]] && search "${search_video}" || continue
        ;;
      3)
        generate_menu "Desenhos" "D" ${C[MN_TOON]}
        ;;
      4)
        generate_menu "Series" "S" ${C[MN_SERS]}
        ;;
    esac
  done
}

main "$@"
